#include "XSkelMesh.h"
#include <fstream>
#include "XLog.h"
#include "../LibJson/json.h"


XSkelMesh::XSkelMesh()
{
	m_back_vertex = NULL;
}


XSkelMesh::~XSkelMesh()
{
	for each (XBone* bone in m_skel)
	{
		SAFE_RELEASE(bone);
	}
	for each (XAnimation* anim in m_animations)
	{
		SAFE_RELEASE(anim);
	}
	if (m_back_vertex != NULL)
		m_back_vertex->Release();
}

bool XSkelMesh::LoadResource(XModFileStream file_stream){
	m_meshes.clear();
	ifstream fs_input;

	//fs_input.open(str_file, ios::in | ios::binary);
	if (!file_stream.IsLoaded()){
		XLog::GetInstance()->LogTo(1, "[Error]Can't Open Mesh File\n");
		return false;
	}

	Json::Reader js_reader;
	Json::Value js_root;
	string data((char*)file_stream.GetData());
	if (js_reader.parse(data, js_root) == false)
		return false;
	string s = XModFile::GetRootPath() + js_root["Mesh"].asString();
	XModFileStream mesh_stream(s);
	if (!mesh_stream.IsLoaded())
		return false;
	VVector* points = NULL;
	int i_points = 0;
	VVertex* vertexs = NULL;
	int i_vertexs = 0;
	VFace* faces = NULL;
	int i_faces = 0;
	VMaterial* materials = NULL;
	int i_materials = 0;
	VBones* bones = NULL;
	int i_bones = 0;
	VWeights* weights = NULL;
	int i_weights = 0;
	XSubMesh** ptr_new_smesh;
	int prev_mat_id = -1;
	while (!mesh_stream.IsEOF()){
		ChunkHeader Chunk;
		ZeroMemory(&Chunk, sizeof(Chunk));
		mesh_stream.Read(&Chunk, sizeof(Chunk));
		string res(Chunk.ChunkID);
		if (res == "ACTRHEAD"){
			//printf("Get PSK File\n");
		}
		if (res == "PNTS0000"){
			i_points = Chunk.DataCount;
			points = (VVector *)malloc(sizeof(VVector)*Chunk.DataCount);
			for (int i = 0; i<Chunk.DataCount; i++){
				mesh_stream.Read(&points[i], sizeof(VVector));
			}

		}
		if (res == "VTXW0000"){
			vertexs = (VVertex*)malloc(sizeof(VVertex)*Chunk.DataCount);
			i_vertexs = Chunk.DataCount;
			for (int i = 0; i<Chunk.DataCount; i++){
				mesh_stream.Read(&vertexs[i], sizeof(VVertex));
			}
		}
		if (res == "FACE0000"){
			faces = (VFace*)malloc(sizeof(VFace)*Chunk.DataCount);
			i_faces = Chunk.DataCount;
			for (int i = 0; i<Chunk.DataCount; i++){
				mesh_stream.Read(&faces[i], sizeof(VFace));
			}
		}
		if (res == "FACE3200"){

		}
		if (res == "MATT0000"){
			materials = (VMaterial*)malloc(sizeof(VMaterial)*Chunk.DataCount);
			i_materials = Chunk.DataCount;
			for (int i = 0; i<Chunk.DataCount; i++){
				mesh_stream.Read(&materials[i], sizeof(VMaterial));
			}
		}
		if (res == "REFSKELT"){
			bones = (VBones*)malloc(sizeof(VBones)*Chunk.DataCount);
			i_bones = Chunk.DataCount;
			for (int i = 0; i<Chunk.DataCount; i++){
				mesh_stream.Read(&bones[i], sizeof(VBones));
			}
		}
		if (res == "RAWWEIGHTS"){
			weights = (VWeights*)malloc(sizeof(VWeights)*Chunk.DataCount);
			i_weights = Chunk.DataCount;
			for (int i = 0; i<Chunk.DataCount; i++){
				mesh_stream.Read(&weights[i], sizeof(VWeights));
			}
		}
	}
	ptr_new_smesh = new XSubMesh*[i_materials];
	for (int i = 0; i < i_materials; i++){
		ptr_new_smesh[i] = new XSubMesh();
	}
	struct Vertex{
	public:
		float x, y, z, u, v;
		Vertex(){

		}
		Vertex(float _x, float _y, float _z){
			x = _x; y = _y; z = _z;
		}

	};
	Vertex* vertex = new Vertex[i_points];
	for (int i = 0; i<i_points; i++){
		vertex[i] = Vertex(points[i].x, points[i].y, points[i].z);
	}
	WORD* ptr_index = new WORD[i_faces * 3];

	for (int i = 0; i<i_faces * 3; i += 3){
		int i_w0, i_w1, i_w2;
		i_w0 = faces[i / 3].Wedge0;
		i_w1 = faces[i / 3].Wedge1;
		i_w2 = faces[i / 3].Wedge2;
		int i_p_index0 = ptr_index[i] = vertexs[i_w0].PointIndex;
		int i_p_index1 = ptr_index[i + 1] = vertexs[i_w1].PointIndex;
		int i_p_index2 = ptr_index[i + 2] = vertexs[i_w2].PointIndex;

		vertex[i_p_index0].u = vertexs[i_w0].U;
		vertex[i_p_index0].v = vertexs[i_w0].V;
		vertex[i_p_index1].u = vertexs[i_w1].U;
		vertex[i_p_index1].v = vertexs[i_w1].V;
		vertex[i_p_index2].u = vertexs[i_w2].U;
		vertex[i_p_index2].v = vertexs[i_w2].V;
		if (prev_mat_id != vertexs[i_w0].MatIndex)
		{
			ptr_new_smesh[vertexs[i_w0].MatIndex]->AddSection();
			ptr_new_smesh[vertexs[i_w0].MatIndex]->m_sections[(ptr_new_smesh[vertexs[i_w0].MatIndex]->m_sections.size() - 1)].m_vertex_begin = -1;
			ptr_new_smesh[vertexs[i_w0].MatIndex]->m_sections[(ptr_new_smesh[vertexs[i_w0].MatIndex]->m_sections.size() - 1)].m_vertex_count = 0;
			ptr_new_smesh[vertexs[i_w0].MatIndex]->m_sections[(ptr_new_smesh[vertexs[i_w0].MatIndex]->m_sections.size() - 1)].m_vertex_prim_count = 0;
		}
		if (ptr_new_smesh[vertexs[i_w0].MatIndex]->m_sections[(ptr_new_smesh[vertexs[i_w0].MatIndex]->m_sections.size() - 1)].m_vertex_begin == -1)
			ptr_new_smesh[vertexs[i_w0].MatIndex]->m_sections[(ptr_new_smesh[vertexs[i_w0].MatIndex]->m_sections.size() - 1)].m_vertex_begin = i;
		ptr_new_smesh[vertexs[i_w0].MatIndex]->m_sections[(ptr_new_smesh[vertexs[i_w0].MatIndex]->m_sections.size() - 1)].m_vertex_count += 3;
		ptr_new_smesh[vertexs[i_w0].MatIndex]->m_sections[(ptr_new_smesh[vertexs[i_w0].MatIndex]->m_sections.size() - 1)].m_vertex_prim_count++;
		prev_mat_id = vertexs[i_w0].MatIndex;
	}
	u_face_count = i_faces;
	u_vert_count = i_vertexs;

	FillMesh((void*)vertex, i_points*sizeof(Vertex), (void*)ptr_index, i_faces * 3 * sizeof(WORD));


	for (int i = 0; i < i_materials; i++){
		ptr_new_smesh[i]->SetTexture(XModFile::GetRootPath() + (js_root["Textures"][i])["file"].asString());
		//if the texture has alpha info then put it into the end of mesh list
		if ((js_root["Textures"][i])["tag"].asString() == "Alpha")
			m_meshes.insert(m_meshes.end(), ptr_new_smesh[i]);
		else
			m_meshes.insert(m_meshes.begin(), ptr_new_smesh[i]);
	}
	for (int i = 0; i < i_bones; i++){
		XBone* new_bone = new XBone();
		new_bone->m_offest=(bones[i].Position);
		new_bone-> m_rotation = (bones[i].Orientation);
		new_bone->m_name = (bones[i].Name);
		new_bone->SetAutoRec();
		if (bones[i].ParentIndex>-1 && bones[i].ParentIndex < i_bones){
			m_skel.insert(m_skel.end(), new_bone);
			if (bones[i].ParentIndex != i){
				new_bone->m_parent = (m_skel[bones[i].ParentIndex]);
			}
			new_bone->UpdateBone();
			new_bone->CalcOriginTransformMatrix();
			
		}
	}
	for (int i = 0; i < i_weights; i++){
		XBone::Weight new_weight;
		new_weight.pointId = weights[i].PointIndex;
		new_weight.weight = weights[i].Weight;
		m_skel[weights[i].BoneIndex]->m_weights.insert(m_skel[weights[i].BoneIndex]->m_weights.end(), new_weight);
	}
	free(faces);
	free(points);
	free(vertexs); 
	free(materials);
	delete[] vertex;
	delete[] ptr_index;
	//delete[] ptr_new_smesh;
	return true;
}

void XSkelMesh::Render(){

	
	//XRender::GetInstance()->SetRenderStream(0, m_vertex, 0, 20);
	//XRender::GetInstance()->SetIndexStream(m_indexed);
	//for each (XSubMesh* ptr_mesh in m_meshes)
	//{
	//	ptr_mesh->Render();
	//}
	int a = 0;
	for each (XBone* bone in m_skel)
	{
		bone->UpdateBone();
		bone->RenderBone();
		
	}
}

vector<XBone*> XSkelMesh::GetSkeleton(){
	return m_skel;
}

void XSkelMesh::AddAnimation(XModFileStream animation){
	if (!animation.IsLoaded())
		return;
	int i_anim_info = 0;
	int i_anim_keys = 0;
	AnimInfo* anim_info = NULL;
	XFrame* anim_keys = NULL;
	while (!animation.IsEOF()){
		ChunkHeader Chunk;
		ZeroMemory(&Chunk, sizeof(Chunk));
		animation.Read(&Chunk, sizeof(Chunk));
		string res(Chunk.ChunkID);
		if (res == "ANIMHEAD"){

		}
		if (res == "BONENAMES"){
			animation.Move(Chunk.DataSize*Chunk.DataCount);
		}
		if (res == "ANIMINFO"){
			i_anim_info = Chunk.DataCount;
			anim_info = (AnimInfo*)malloc(Chunk.DataCount*sizeof(AnimInfo));
			for (int i = 0; i < Chunk.DataCount; i++){
				animation.Read(&anim_info[i], sizeof(AnimInfo));
			}
		}
		if (res == "ANIMKEYS"){
			i_anim_keys = Chunk.DataCount;
			anim_keys = (XFrame*)malloc(Chunk.DataCount*sizeof(XFrame));
			for (int i = 0; i < Chunk.DataCount; i++){
				animation.Read(&anim_keys[i], sizeof(XFrame));
			}
		}

	}
	int i_pass_frames = 0;
	for (int i = 0; i < i_anim_info; i++){
		XAnimation* new_anim = new XAnimation();
		new_anim->InsertFrames(anim_keys + i_pass_frames, anim_info[i].KeyQuotum);
		new_anim->SetAnimationName(anim_info[i].Name);
		new_anim->SetAutoRec();
		new_anim->SetTotalBones(anim_info[i].TotalBones);
		new_anim->SetAnimationRate(anim_info[i].AnimRate);
		AddAnimation(new_anim);
		i_pass_frames += anim_info[i].KeyQuotum;
	}
	free(anim_info);
}

void XSkelMesh::AddAnimation(XAnimation* animation){
	m_animations.insert(m_animations.end(), animation);
}

void XSkelMesh::PlayNextFrame(int anim_id){
	if (1)
		return;
	//m_animations[anim_id]->PlayNextFrame(m_vertex,m_skel);
	m_animations[anim_id]->UpdateSkeleton(m_skel, 0);
	Vector3* vertexs,* vertexs_bak;
	m_vertex->GetSourceBuffer((void**)&vertexs);
	m_back_vertex->GetSourceBuffer((void**)&vertexs_bak);
	for each (XBone* bone in m_skel)
	{
		for each (XBone::Weight weight in bone->m_weights)
		{
			vertexs[weight.pointId] = vertexs_bak[weight.pointId] * bone->GetTransformMatrix()*weight.weight;
		}
	}
	m_vertex->SetSourceBuffer(vertexs, 0, m_vertex->GetSize());
	free(vertexs);
	free(vertexs_bak);
}